
//**************************************************************************/
// Copyright (c) 2010 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: August 2010
//**************************************************************************/

#include "PtexLayout.h"
#include <QtGui/QWidget>
#include <QtGui/QGridLayout>

// RTTI macro, needed for each class.
IMPLEMENT_SCLASS( PtexLayout, Layout, "ptexlayout", 2 );

// Default constructor, initializes the quality to 0.5, which means 32x32=1024 reference points will be generated for each base level face.
PtexLayout::PtexLayout( void ) :
	m_eQuality( this, "quality" ) 
{ 
	m_eQuality.SetName( QObject::tr("Quality:") );
	m_eQuality.SetToolTip( QObject::tr("Mudbox automatically calculates a good per-face resolution for your ptex file.  Use this dropdown to choose a lower resolution (saves disk space) or a higher one (provides more detail).") );
	m_eQuality.AddItem( QObject::tr("Low") );
	m_eQuality.AddItem( QObject::tr("Good") );
	m_eQuality.AddItem( QObject::tr("High") );
	m_eQuality = 1;
	m_iDirectResolution = -1;
};

// Create the user interface widget for the layout, which consists of a single widget at this moment, the quality slider.
QWidget *PtexLayout::UserInterface( void )
{ 
	QWidget *w = new QWidget;
	QGridLayout *l = new QGridLayout;
	// If there are multiple attributes, they can be added to the QGridLayout object the same way. All added attribute will appear on the UI in a 
	// different row.
	l->addWidget( m_eQuality.CreateEditorWidget( NULL, 150 ) );
	w->setLayout( l );		  
	return w;
};

// Main function, it processes the list of the given target surfaces, and collects the reference points on them.
void PtexLayout::ProcessSurfaces( QVector<SubdivisionLevel *> aSurfaces )
{
	// Loop through the target meshes.
	for ( int s = 0; s < aSurfaces.size(); s++ )
	{
		SubdivisionLevel *t = aSurfaces[s];

		// Indicate that a section of reference points has begun. Each mesh will have its own section.
		BeginSection();

		if ( t->Type() == Mesh::typeQuadric )
		{
			// Decide the horizontal and vertical resolution of the grid what we are using for the face. Currently all the faces got the same resolution, but
			// the ptex file format can handle changing grid sizes for the faces.
			int iRes = TargetResolution( s );
			int iHRes = 1 << iRes;
			int iVRes = 1 << iRes;
				
			// Loop through all the faces of the mesh.
			for ( unsigned int f = 0; f < t->FaceCount(); f++ )
			{
				// Loop through the points of the grid.
				for ( int u = 0; u < iHRes; u++ )
					for ( int v = 0; v < iVRes; v++ )
					{
						// For each point on the grid we create a reference point, and call the ProcessSurfacePoint function.
						TargetLocation l;
						l.m_aLayoutData[dataURes] = u+(iRes<<24);
						l.m_aLayoutData[dataVRes] = v+(iRes<<24);
						l.m_aLayoutData[dataFaceID] = f;
						
						// To properly define the point on the surface, we use the index of the face, and the coordinates inside the face calculated from the grid coordinate.
						l.Fill( t, f, (u+0.5f)/(float)iHRes, (v+0.5f)/(float)iVRes );
						
						ProcessSurfacePoint( l );
					};
			};
			// Declaring the end of the section, which will tell the PtexUtilizer objects that no more reference points will be generated for the current mesh, so they can finish
			// writing the ptex file into the disk.
		}
		else
		{
			PrepareAdjacency( t );
			unsigned int iRes = TargetResolution( s );

			unsigned int iFaceID = 0;
			// NSided case, this is more complicated than the quad one.
			for ( unsigned int f = 0; f < t->FaceCount(); f++ )
			{
				// We go through all the polygons of the target mesh, so we skip fake triangles.
				if ( t->IsFakeTriangle( f ) )
					continue;

				// Calculate the number of sides for this poly.
				int iSideCount = 3, i = f;
				while ( f < t->FaceCount() && t->IsFakeTriangle( ++i ) )
					iSideCount++;

				// The number of the ptex faces generated for this polygon depends on the number of sides. For quads, we have 
				// to generate a single ptex face, for other polygons we generate one ptex face for each vertex, so the number
				// of ptex faces in that case is the same as the number of sides in the polygon.
				int iSubFaceCount = iSideCount;
				if ( iSideCount == 4 )
					iSubFaceCount = 1;

				// Collect the object space position of the corners of the polygon.
				Vector aCorners[16];
				MB_ONBUG( iSideCount >= 16 )
					continue;
				aCorners[0] = t->TriangleVertexPosition( f, 0 );
				aCorners[1] = t->TriangleVertexPosition( f, 1 );
				for ( int i = 0; i < iSideCount-2; i++ )
					aCorners[i+2] = t->TriangleVertexPosition( f+i, 2 );

				// Calculate the position of the center of the polygon/
				Vector vCenter;
				for ( int j = 0; j < iSideCount; j++ )
					vCenter += aCorners[j];
				vCenter *= 1/(float)iSideCount;

				// Go through the subfaces.
				for ( int iSubFace = 0; iSubFace < iSubFaceCount; iSubFace++ )
				{
					Vector vCorner0, vCorner1, vCorner2, vCorner3;
					int iURes = iRes, iVRes = iRes;
					if ( iSideCount == 4 )
					{
						// When this polygon is a quad, then there is a single ptex face, which covers the full area of the quad.
						MB_ASSERT( iSubFace == 0 );
						vCorner0 = aCorners[0];
						vCorner1 = aCorners[1];
						vCorner2 = aCorners[2];
						vCorner3 = aCorners[3];
					}
					else
					{
						// When the polygon is not a quad, then each vertex will have its own ptex face. This ptex face will
						// cover a rectangular area surrounded by the vertex, the center of the two edges attached to the
						// vertex, and the center of the polygon.
						iURes--;		// Since this is a subface, we halve the resolution.
						iVRes--;
						vCorner0 = aCorners[iSubFace];
						vCorner1 = (vCorner0+aCorners[(iSubFace+1)%iSideCount])*0.5f;
						vCorner2 = vCenter;
						vCorner3 = (vCorner0+aCorners[(iSubFace-1+iSideCount)%iSideCount])*0.5f;
					};
					// Now that we have the object space position of the corners of the ptex face, we generate reference
					// points in that area in a grid layout.
					int iUSize = 1 << iURes, iVSize = 1 << iVRes;
					for ( int iU = 0; iU < iUSize; iU++ )
						for ( int iV = 0; iV < iVSize; iV++ )
						{
							TargetLocation l;
							l.m_aLayoutData[dataURes] = iU+(iURes<<24);
							l.m_aLayoutData[dataVRes] = iV+(iVRes<<24);
							l.m_aLayoutData[dataFaceID] = iFaceID;
							if ( iSubFaceCount > 1 )
								l.m_aLayoutData[dataFaceID] |= 0x80000000;
							
							float fU = ((float)iU+0.5f)/iUSize, fV = ((float)iV+0.5f)/iVSize;
							Vector vH0 = vCorner0+(vCorner1-vCorner0)*fU;
							Vector vH1 = vCorner3+(vCorner2-vCorner3)*fU;
							l.FillNSided( t, f, vH0+(vH1-vH0)*fV );	// We initialize the location based on the object space position of the reference point.
							ProcessSurfacePoint( l );
						};
					iFaceID++;
				};
			};
		};
		EndSection();
	};
};

// This function prepares the object for the extraction, and returns the number of the expected reference points.
unsigned int PtexLayout::Prepare( void )
{
	// Count the total number of reference points which will be used during the extraction.
	int iRefCount = 0;
	for ( int c = 0; c < m_pMapExtractor->TargetContainerCount(); c++ )
		for ( int m = 0; m < m_pMapExtractor->TargetMeshCountInContainer( c ); m++ )
		{
			const Mesh *p = m_pMapExtractor->TargetMesh( c, m );
			// For full quad meshes, the number of the generated ptex faces is equals to the number of quads in the mesh.
			if ( p->Type() == Mesh::typeQuadric )
				iRefCount += m_pMapExtractor->TargetMesh( c, m )->FaceCount()*(1<<(2*TargetResolution( c )));
			else
			{
				// For other meshes we have to check all the polygons, and calculate the number of ptex faces.
				float t = 0;
				for ( unsigned int i = 0; i < p->FaceCount(); i++ )
				{
					if ( p->IsFakeTriangle( i ) )
						continue;
					unsigned int f = i+1;
					while ( f < p->FaceCount() && p->IsFakeTriangle( f ) )
						f++;
					unsigned int s = f-i+2;
					// Quads will have a single ptex face, other polygons will have as much ptex faces as much sides
					// they have, but with half resolution (i.e. the number of reference points for those subfaces is quarter
					// of the normal ones.
					if ( s == 4 )
						t++;
					else
						t += 0.25*s;
				};
				iRefCount += t*(1<<(2*TargetResolution( c )));
			};
		};
	
	// The number of expected is the multiplication of the two, since all the faces will get the same number of reference points.
	return iRefCount;
};

// This function calculates the needed resolution for the generated ptex file. The returned value indicates the level used for each face (0=1x1, 1=2x2, 2=4x4, 3=16x16, ... )
int PtexLayout::TargetResolution( int iTargetIndex ) const
{
	int iRes = 0;

	if ( m_iDirectResolution != -1 )
		return m_iDirectResolution;

	// If the number of target and source meshes are the same, we calculate the needed resolution based on the ratio between their face count.
	if ( m_pMapExtractor->TargetContainerCount() == (int)m_pMapExtractor->SourceCount() && m_pMapExtractor->TargetMeshCountInContainer( iTargetIndex ) == 1 )
	{
		int iFaceCountFactor = m_pMapExtractor->Source( iTargetIndex )->FaceCount()/m_pMapExtractor->TargetMesh( iTargetIndex, 0 )->FaceCount();
		while ( iFaceCountFactor > 1 )
		{
			iRes++;
			iFaceCountFactor /= 4;
		};
	}
	else
		iRes = 4;

	// Apply the quality attribute.
	if ( m_eQuality == 0 )
		iRes--;
	if ( m_eQuality == 2 )
		iRes++;

	return Max(1,Min(10,iRes));
};

// Serialize the state of the object. We only need to serialize the only one attribute which belongs to this class.
void PtexLayout::Serialize( Stream &s )
{
	if ( s.IsNewerThan( 0, this ) )
	{
		if ( s.IsNewerThan( 1, this ) )
			s == m_eQuality;
		else
		{
			float f;
			s >> f;
		};
	};
	Layout::Serialize( s );
};

// Precalculating the m_aFaceID and m_aTriangle arrays for the current mesh. This is only needed when the target mesh is not a full quad mesh.
void PtexLayout::PrepareAdjacency( const Mesh *pMesh )
{
	// The m_aFaceID array will containt the ID of the first ptex face which belongs to the given triangle. 
	// For example, if a pentagon represented by the triangles 5-6-7 is split into ptex face with the ID of 7-11, then the array will contain values:
	// m_aFaceID[5] = 7		// ID of the first ptex face belongs to this polygon
	// m_aFaceID[6] = -1		// because this is a fake triangle
	// m_aFaceID[7] = -1     // the same
	// m_aFaceID[8] = 12		// this belongs to the next polygon
	m_pMesh = pMesh;
	m_aFaceID.resize( pMesh->FaceCount() );
	unsigned int iTFaceID = 0;	// This is the ID of the current ptex face.
	for ( unsigned int i = 0; i < pMesh->FaceCount(); i++ )
	{
		MB_ASSERT( !pMesh->IsFakeTriangle( i ) );
		m_aFaceID[i] = iTFaceID;

		// Calculate the sides of the polygon.
		int s = 3;
		while ( i+1 < pMesh->FaceCount() && pMesh->IsFakeTriangle( i+1 ) )
		{
			i++;
			s++;
			m_aFaceID[i] = 0xffffffff;
		};

		// If the polygon is a quad, then a single ptex face will be generated for it, otherwise each corner will get its own ptex face.
		if ( s == 4 )
			iTFaceID++;
		else
			iTFaceID += s;
	};

	// Fill the m_aTriangle array, which is the opposite of the m_afaceID array, it contains the index of the triangle which represents
	// the polygon which belongs to the current ptex face. In the same example as above, the array will look like this:
	// m_aTriangle[7] = 5
	// m_aTriangle[8] = 5
	// m_aTriangle[9] = 5
	// m_aTriangle[10] = 5
	// m_aTriangle[11] = 5
	// m_aTriangle[12] = 8	// this belongs to the next polygon
	m_aTriangle.fill( 0xffffffff, iTFaceID );
	for ( unsigned int i = 0; i < m_aFaceID.size(); i++ )
		if ( m_aFaceID[i] != 0xffffffff )
			m_aTriangle[m_aFaceID[i]] = i;

	for ( unsigned int i = 1; i < m_aTriangle.size(); i++ )
		if ( m_aTriangle[i] == 0xffffffff )
			m_aTriangle[i] = m_aTriangle[i-1];
};

// This function calculates the adjacency info for an edge of a mudbox triangle. The iSegment parameter controls which part of the
// edge we are interested (0=first half, 1=second half). This function is only needed when the target mesh is not a full quad mesh.
unsigned int PtexLayout::AdjacentFaceForTriangle( unsigned int iFaceIndex, unsigned int iSide, unsigned int iSegment, unsigned int &iEdge ) const
{
	// Check which is the adjacent triangle (if any)
	MB_ONBUG( iFaceIndex >= m_pMesh->FaceCount() )
		return 0xffffffff;
	unsigned int a = m_pMesh->TriangleAdjacency( iFaceIndex, iSide );
	if ( a == 0xffffffff )
		return 0xffffffff;

	// And calculate the side count for the polygon the triangle represents.
	unsigned int t = a/3;
	MB_ONBUG( t >= m_pMesh->FaceCount() )
		return 0xffffffff; 
	while ( m_pMesh->IsFakeTriangle( t ) )
	{
		MB_ONBUG( t == 0 )
			return 0xffffffff;
		t--;
	};
	unsigned int h = t+1;
	while ( h < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle( h ) )
		h++;
	unsigned int s = h-t+2;

	MB_ONBUG( s >= 16 )
		return 0xffffffff;

	// Collect vertices of the adjacent polygon into a local array.
	unsigned int iV[16];
	iV[0] = m_pMesh->TriangleIndex( t, 0 );
	iV[1] = m_pMesh->TriangleIndex( t, 1 );
	for ( int f = 0; f < s-2; f++ )
		iV[f+2] = m_pMesh->TriangleIndex( t+f, 2 );
	unsigned int iA = m_pMesh->TriangleIndex( iFaceIndex, (iSide+1)%3 ), iB = m_pMesh->TriangleIndex( iFaceIndex, (iSide+2)%3 );

	// Search which side of the adjacent poly we are.
	unsigned int as = 0;
	while ( iB != iV[as] && iA != iV[(as+1)%s] )
	{
		as++;
		MB_ONBUG( as == s )
			return 0xffffffff;
	};

	// Quads are special case, since they are a single ptex face. In this case the iSegment parameter is ignored.
	if ( s == 4 )
	{
		iEdge = as;
		return m_aFaceID[t];
	};

	// For other polygons, the edge depends on the iSegment value.
	if ( iSegment )
	{
		as = (as+1)%s;
		iEdge = 3;
	}
	else
		iEdge = 0;
	return m_aFaceID[t]+as;
};

// This function calculates the adjacency info for an edge of a ptex face. If the edge is an internal edge of a polygon, the
// function calculates the values directly. In other cases it calls the function AdjacentFaceForTriangle. This function is only 
// needed when the target mesh is not a full quad mesh.
unsigned int PtexLayout::AdjacentFace( unsigned int iFaceID, unsigned int iSide, unsigned int &iEdge ) const
{
	// Get the index of the triangle which represents the polygon of the ptex face. This must be a real triangle (i.e. the first triangle of 
	// that polygon).
	unsigned int iFaceIndex = m_aTriangle[iFaceID];
	MB_ONBUG( m_pMesh->IsFakeTriangle( iFaceIndex ) )
		return 0xffffffff;

	// Calculate the number of sides of that polygon.
	unsigned int e = iFaceIndex+1;
	while ( e < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle( e ) )
		e++;
	unsigned int s = e-iFaceIndex+2;

	// If the polygon is a quad, there are no internal edges (since all quads are represented as a single ptex face),
	// so we call AdjacentFaceForTriangle. If the other side of the edge contains multiple ptex faces (i.e. it belongs to
	// a polygon which is not a quad), ptex expects the first subface encountered in a counter-clockwise (i.e. edgeid 
	// order) traversal of the face as the adjacent face, so we are looking for the second segment of the edge (iSegment=1)
	if ( s == 4 )
	{
		unsigned int b[4] = { 0, 0, 1, 1 };
		unsigned int s[4] = { 2, 0, 0, 1 };
		return AdjacentFaceForTriangle( iFaceIndex+b[iSide], s[iSide], 1, iEdge );
	};

	// When the current polygon is not a quad, then the edges 0 and 3 are external edges (see http://http://ptex.us/adjdata.html 
	// for more detail), so in that case we call AdjacentFaceForTriangle with the proper data.
	if ( iSide == 0 || iSide == 3 )
	{
		// First we calculate which edge of the polygon this ptex edge belongs to.
		unsigned int l = iFaceID-m_aFaceID[iFaceIndex];
		MB_ONBUG( l >= 16 )
			return 0xffffffff;
		if ( iSide )
			l = (l+s-1)%s;

		// If it is the first edge, then that is the last edge of the first triangle belongs to the polygon.
		if ( l == 0 )
			return AdjacentFaceForTriangle( iFaceIndex, 2, iSide ? 0 : 1, iEdge );
		// If it is the last edge, then it is the middle edge of the last triangle.
		if ( l == s-1 )
			return AdjacentFaceForTriangle( iFaceIndex+s-3, 1, iSide ? 0 : 1, iEdge );
		// In other cases, it is the first edge of the corresponding triangle.
		return AdjacentFaceForTriangle( iFaceIndex+l-1, 0, iSide ? 0 : 1, iEdge );
	};

	// When it is an internal edge, the adjacent ptex face will be another subface of the same polygon.
	if ( iSide == 2 )
		iEdge = 1;
	else
		iEdge = 2;
	return m_aFaceID[iFaceIndex]+((iFaceID-m_aFaceID[iFaceIndex]+(iSide==2 ? -1 : 1)+s)%s);
};